package main

import (
	"context"
	"fmt"
	"redisflock/config"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/redis/go-redis/v9"
)

func main() {

	r := gin.Default()
	r.GET("/test", func(c *gin.Context) {
		uuidValue := (uuid.New()).String()
		ttl := 30

		nxlock(uuidValue, ttl)
		//业务
		time.Sleep(2 * time.Second)

		unlock(uuidValue)
	})

	r.Run()
}

// 抢锁
func nxlock(uuidValue string, ttl int) {

	var incrBy = redis.NewScript(`
	--Lua 脚本
	local key = KEYS[1]     						-- 获取第一个键参数
	local value = ARGV[1]                        	-- 获取第一个值参数
	local ttl = ARGV[2] 							-- 获取第二个值参数
	local result = redis.call("SETNX", key, value)  -- 使用 redis.call 来执行 SETNX 命令
	if result == 1 then
	 redis.call("EXPIRE", key, ttl)
	 end
	return result 									 -- 返回结果
	`)

	result, err := incrBy.Run(config.Ctx, config.Rdb, []string{"keyz"}, uuidValue, ttl).Int()
	if err != nil {
		fmt.Println("Error:", err)
	} else {
		if result == 0 {
			fmt.Println("抢锁失败")
			//超时配置
			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(ttl)*time.Second)
			defer cancel()
			//订阅
			pubsub := config.Rdb.Subscribe(ctx, "keyz")
			defer pubsub.Close()
			_, err := pubsub.ReceiveMessage(ctx) //阻塞订阅
			if err != nil {
				// 检查是否是超时错误
				if err == context.DeadlineExceeded {
					fmt.Println("接收消息超时")
					//(自旋)
					nxlock(uuidValue, ttl)
				} else {
					fmt.Printf("接收消息失败: %v\n", err)
				}
				return
			}
			//(自旋)
			nxlock(uuidValue, ttl)

			// fmt.Println(msg.Channel, msg.Payload)
		}

		if result == 1 {
			fmt.Println("抢锁成功")
			go watchdog(uuidValue, ttl)
		}
	}
}

// 解锁
func unlock(uuidValue string) {
	var incrBy = redis.NewScript(`
	--Lua 脚本
	local key = KEYS[1]                    -- 获取第一个键参数
	local value = ARGV[1]                  -- 获取第一个值参数
	local uuidvalue = redis.call("GET", key)
	local result
	if uuidvalue == value then
	result = redis.call("DEL", key)  -- 删除锁
	else
	result = 0
	end
	return result 						   -- 返回结果
	`)

	result, err := incrBy.Run(config.Ctx, config.Rdb, []string{"keyz"}, uuidValue).Int()
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	if result == 1 {
		fmt.Println("删锁成功")
		// 发布消息
		err := config.Rdb.Publish(config.Ctx, "keyz", "unlock").Err()
		if err != nil {
			panic(err)
		}
	}
}

// 看门狗
func watchdog(uuidValue string, ttl int) {

	for i := 0; i < 3; i++ {
		time.Sleep(time.Duration(ttl/2) * time.Second)
		var incrBy = redis.NewScript(`
		--Lua 脚本
		local key = KEYS[1]     						-- 获取第一个键参数
		local value = ARGV[1]                        	-- 获取第一个值参数
		local ttl = ARGV[2] 							-- 获取第二个值参数
		local uuidvalue = redis.call("GET", key)
		local result
		if uuidvalue == value then
		result = redis.call("EXPIRE", key, ttl)
		else
		result = 0
		end
		return result 						   			-- 返回结果
	    `)
		result, err := incrBy.Run(config.Ctx, config.Rdb, []string{"keyz"}, uuidValue, ttl).Int()
		if err != nil {
			fmt.Println("Error:", err)
			return
		}
		if result == 0 {
			break
		} else {
			fmt.Println("看门狗续期成功:", result)
		}

	}

}
